/* Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
/* File for "A Sample Game: Crab Pong" lesson of the OpenGL tutorial on
 * www.videotutorialsrock.com
 */

#include <cstdlib>
#include <ctime>
#include <iostream>
#include <math.h>
#include <stdlib.h>
#include <vector>
#include <sstream>

#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#include "text3d.h"
#include "Player.h"
#include "Bullet.h"
#include "EnemyManager.h"

#endif

using namespace std;


//The number of milliseconds between calls to update
const int TIMER_MS = 17;

//Whether the left key is currently depressed
bool isLeftKeyPressed = false;
//Whether the right key is currently depressed
bool isRightKeyPressed = false;
//Whether the left key is currently depressed
bool isZKeyPressed = false;
int gameState = 0; // 0 = startScreen 1 = Playing 2 = Game Over
Player* thePlayer;
EnemyManager* theEnemyManager;
vector<Bullet> theBullets;

float shakeRot = 0.0f;
bool shakedRight = false;
bool shakedLeft = false;
bool shaking = false;

int theScore = 0;
// Maxwidth = 30;
// Maxhieght = 22;

void displayStartScreen(){
       string ss = "SPACE INVADERS\nPress Space Bar to Start";
       glTranslatef(0.0f, 0.0f, 0.0f);
       t3dDraw2D(ss, 0.0f, 0.0f, 2.0f);
       glPopMatrix();
       glPushMatrix();
}

void displayGameOver(){
       string ss = "GAME OVER\nPress Space Bar to Play Again";
       glTranslatef(0.0f, 0.0f, 0.0f);
       t3dDraw2D(ss, 0.0f, 0.0f, 2.0f);
       glPopMatrix();
       glPushMatrix();
}

void handleKeypress(unsigned char key, int x, int y) {
	switch (key) {
		   case 'z':
				   if(gameState == 1){
						   isZKeyPressed = true;
						   if (thePlayer->GetLives()>0)
								   theBullets.push_back(thePlayer->Shoot());
				   }
				   break;
		   
		   case 32:
				   if(gameState != 1)
						gameState = 1;
				   break;
		   case 27: //Escape key
				   exit(0);
	}
}
void handleKeyReleased(unsigned char key, int x, int y) {
	switch (key) {
		case 'z':
			isZKeyPressed = false;
	}
}

void handleSpecialKeypress(int key, int x, int y) {
	switch (key) {
		   case GLUT_KEY_LEFT:
				   if(gameState == 1){
						   isLeftKeyPressed = true;
						   if (thePlayer->GetLives()>0){
								   if (isRightKeyPressed)
										   thePlayer->SetDirection(0);
								   else
										   thePlayer->SetDirection(-1);
						   }
				   }
				   break;
		   case GLUT_KEY_RIGHT:
				   if(gameState == 1){
						   isRightKeyPressed = true;
						   if (thePlayer->GetLives()>0){
								   if (isLeftKeyPressed)
										   thePlayer->SetDirection(0);
								   else
										   thePlayer->SetDirection(1);
						   }
				   }
				   break;
		   
	}
}

void handleSpecialKeyReleased(int key, int x, int y) {
	switch (key) {
		case GLUT_KEY_LEFT:
			isLeftKeyPressed = false;
			if (thePlayer->GetLives()>0){
				if (isRightKeyPressed)
					thePlayer->SetDirection(1);
				else
					thePlayer->SetDirection(0);
			}
			break;
		case GLUT_KEY_RIGHT:
			isRightKeyPressed = false;
			if (thePlayer->GetLives()>0){
				if (isLeftKeyPressed)
					thePlayer->SetDirection(-1);
				else
					thePlayer->SetDirection(0);
			}
			break;
	}
}

void initRendering() {
	glClearColor(0.7f, 0.9f, 1.0f, 1.0f); 
       GLfloat light_position[] = {5.0, 0.0, -5.0, 0.0 };
       GLfloat ambient[] = { 1.0, 1.0, 1.0, 0.0 };
       glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
       glLightfv(GL_LIGHT0, GL_POSITION, light_position);
       glEnable(GL_DEPTH_TEST);
       glEnable(GL_COLOR_MATERIAL);
       glEnable(GL_LIGHTING);
       glEnable(GL_LIGHT0);
       glEnable(GL_NORMALIZE);
       glEnable(GL_CULL_FACE);
       glShadeModel(GL_SMOOTH);
       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
       t3dInit();
}

void DisplayText(){
	// Score
	stringstream out;
	out << theScore;
	string ss = "Score: " + out.str();
	glTranslatef(-2.0f, 23.0f, 0.0f); //Move Upward
	t3dDraw2D(ss, 0.0f, 0.0f, 2.0f);
	glPopMatrix(); //Undo the move
	glPushMatrix(); //Save the current state 

	// Lives
	stringstream out2;
	out2 << thePlayer->GetLives();
	ss = "Lives: " + out2.str();
	glTranslatef(15.0f, 23.0f, 0.0f); //Move Upward
	t3dDraw2D(ss, 0.0f, 0.0f, 2.0f);
	glPopMatrix(); //Undo the move
	glPushMatrix(); //Save the current state 
}
void drawScene() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
	glLoadIdentity(); //Reset the drawing perspective
	glTranslatef(0.0f, 0.0f, -60.0f); //Move forward 5 units
	//glRotatef(-15, 1.0f, 0.0f, 0.0f); //Rotate the camera
	glPushMatrix();

	//Game is in start screen
	if(gameState == 0)
		   displayStartScreen();

	else if(gameState == 1){
		   thePlayer->Draw();
		   theEnemyManager->Draw();
		   for (int i = 0; i< theBullets.size();i++){
				   theBullets.at(i).Draw();
		   }
		   // Display Text
		   DisplayText();
	}

	else if(gameState == 2)
		   displayGameOver();

	glutSwapBuffers();
}

void shakeCamera(int value){
       if(!shakedRight){
               shakeRot += 15;
               if(shakeRot >= 90){
                       shakeRot = 90;
                       shakedRight = true;
               }
       }

       else if(!shakedLeft){
               shakeRot -= 15;
               if(shakeRot <= -90){
                       shakeRot = -90;
                       shakedLeft = true;
               }
       }

       else{
               shakeRot += 15;
               if(shakeRot >= 0){
                       shakeRot = 0;
                       shakedRight = false;
                       shakedLeft = false;
                       shaking = false;
               }
       }

       glRotatef(shakeRot, 0.0f, 0.0f, 1.0f);
       if(shaking)
               glutTimerFunc(TIMER_MS, shakeCamera,0);
       else{
               thePlayer->Reset();
				theBullets.clear();
				thePlayer->SetHasShoot(false);
       }
}

void CheckCollisions()
{
       // Enemy & Bullet Collision
       for (int i=0; i<theEnemyManager->myEnemiesA.size();i++){
               for (int k=0; k<theBullets.size();k++){
                       if (theBullets.at(k).GetDirection()>0 &&
theEnemyManager->myEnemiesA.at(i).GetBoundingBox().isCollision(&theBullets.at(k).GetBoundingBox())){
                               theScore += theEnemyManager->myEnemiesA.at(i).GetPoints();
                               theBullets.erase(theBullets.begin()+k);
                               theEnemyManager->myEnemiesA.erase(theEnemyManager->myEnemiesA.begin()+i);
                               thePlayer->SetHasShoot(false);
                               i--;
                               k--;
                               break;
                       }
               }
       }
       for (int i=0; i<theEnemyManager->myEnemiesB.size();i++){
               for (int k=0; k<theBullets.size();k++){
                       if (theBullets.at(k).GetDirection()>0 &&
theEnemyManager->myEnemiesB.at(i).GetBoundingBox().isCollision(&theBullets.at(k).GetBoundingBox())){
                               theScore += theEnemyManager->myEnemiesA.at(i).GetPoints();
                               theBullets.erase(theBullets.begin()+k);
                               theEnemyManager->myEnemiesB.erase(theEnemyManager->myEnemiesB.begin()+i);
                               thePlayer->SetHasShoot(false);
                               i--;
                               k--;
                               break;
                       }
               }
       }
       for (int i=0; i<theEnemyManager->myEnemiesC.size();i++){
               for (int k=0; k<theBullets.size();k++){
                       if (theBullets.at(k).GetDirection()>0 &&
theEnemyManager->myEnemiesC.at(i).GetBoundingBox().isCollision(&theBullets.at(k).GetBoundingBox())){
                               theScore += theEnemyManager->myEnemiesA.at(i).GetPoints();
                               theBullets.erase(theBullets.begin()+k);
                               theEnemyManager->myEnemiesC.erase(theEnemyManager->myEnemiesC.begin()+i);
                               thePlayer->SetHasShoot(false);
                               i--;
                               k--;
                               break;
                       }
               }
       }
       for (int i=0; i<theEnemyManager->myEnemiesD.size();i++){
               for (int k=0; k<theBullets.size();k++){
                       if (theBullets.at(k).GetDirection()>0 &&
theEnemyManager->myEnemiesD.at(i).GetBoundingBox().isCollision(&theBullets.at(k).GetBoundingBox())){
                               theScore += theEnemyManager->myEnemiesA.at(i).GetPoints();
                               theBullets.erase(theBullets.begin()+k);
                               theEnemyManager->myEnemiesD.erase(theEnemyManager->myEnemiesD.begin()+i);
                               thePlayer->SetHasShoot(false);
                               i--;
                               k--;
                               break;
                       }
               }
       }
       for (int i=0; i<theEnemyManager->myEnemiesE.size();i++){
               for (int k=0; k<theBullets.size();k++){
                       if (theBullets.at(k).GetDirection()>0 &&
theEnemyManager->myEnemiesE.at(i).GetBoundingBox().isCollision(&theBullets.at(k).GetBoundingBox())){
                               theScore += theEnemyManager->myEnemiesA.at(i).GetPoints();
                               theBullets.erase(theBullets.begin()+k);
                               theEnemyManager->myEnemiesE.erase(theEnemyManager->myEnemiesE.begin()+i);
                               thePlayer->SetHasShoot(false);
                               i--;
                               k--;
                               break;
                       }
               }
       }

       // Player & Bullet Collision
      for (int i=0; i<theBullets.size();i++){
              if (thePlayer->GetBoundingBox().isCollision(&theBullets.at(i).GetBoundingBox())){
				  thePlayer->Reset();
				  theBullets.clear();
				  thePlayer->SetHasShoot(false);

                                 /*shaking = true;
                                 shakeCamera(0);*/
                      i--;
                      break;
              }
      }

       // Enemy & Player Collision
       for(int i = 0; i < theEnemyManager->myEnemiesA.size(); i++){
               if(thePlayer->GetBoundingBox().isCollision(&theEnemyManager->myEnemiesA.at(i).GetBoundingBox())){
                       theScore += theEnemyManager->myEnemiesA.at(i).GetPoints();
                       thePlayer->Reset();
                       theEnemyManager->myEnemiesA.erase(theEnemyManager->myEnemiesA.begin() + i);
                       thePlayer->SetHasShoot(false);
                       break;
               }
       }

       for(int i = 0; i < theEnemyManager->myEnemiesB.size(); i++){
               if(thePlayer->GetBoundingBox().isCollision(&theEnemyManager->myEnemiesB.at(i).GetBoundingBox())){
                       theScore += theEnemyManager->myEnemiesB.at(i).GetPoints();
                       thePlayer->Reset();
                       theEnemyManager->myEnemiesB.erase(theEnemyManager->myEnemiesB.begin() + i);
                       thePlayer->SetHasShoot(false);
                       break;
               }
       }

       for(int i = 0; i < theEnemyManager->myEnemiesC.size(); i++){
               if(thePlayer->GetBoundingBox().isCollision(&theEnemyManager->myEnemiesC.at(i).GetBoundingBox())){
                       theScore += theEnemyManager->myEnemiesC.at(i).GetPoints();
                       thePlayer->Reset();
                       theEnemyManager->myEnemiesC.erase(theEnemyManager->myEnemiesC.begin() + i);
                       thePlayer->SetHasShoot(false);
                       break;
               }
       }

       for(int i = 0; i < theEnemyManager->myEnemiesD.size(); i++){
               if(thePlayer->GetBoundingBox().isCollision(&theEnemyManager->myEnemiesD.at(i).GetBoundingBox())){
                       theScore += theEnemyManager->myEnemiesD.at(i).GetPoints();
                       thePlayer->Reset();
                       theEnemyManager->myEnemiesD.erase(theEnemyManager->myEnemiesD.begin() + i);
                       thePlayer->SetHasShoot(false);
                       break;
               }
       }

       for(int i = 0; i < theEnemyManager->myEnemiesE.size(); i++){
               if(thePlayer->GetBoundingBox().isCollision(&theEnemyManager->myEnemiesE.at(i).GetBoundingBox())){
                       theScore += theEnemyManager->myEnemiesE.at(i).GetPoints();
                       thePlayer->Reset();
                       theEnemyManager->myEnemiesE.erase(theEnemyManager->myEnemiesE.begin() + i);
                       thePlayer->SetHasShoot(false);
                       break;
               }
       }

}



void update(int value) {
	if(gameState == 1){
		if (theEnemyManager->IsEmpty())
			   theEnemyManager->Initialize();
		theBullets.push_back(theEnemyManager->Update(TIMER_MS));
		thePlayer->Update(TIMER_MS);


		vector<Bullet>::iterator it = theBullets.begin();
		while ( it != theBullets.end() )
		{
					   Bullet &obj = *it;
					   if (!obj.Update(TIMER_MS)){
							   if (obj.GetDirection()>0)
									   thePlayer->SetHasShoot(false);
							   it = theBullets.erase(it);
					   }
					   else
							   ++it;
		}
		CheckCollisions();
		if(thePlayer->GetLives() < 1){
			gameState = 2;
			thePlayer = new Player();
			theEnemyManager->Initialize();
			theBullets.clear();
			thePlayer->SetHasShoot(false);
			theScore = 0;
		}
	}
	glutPostRedisplay();
	glutTimerFunc(TIMER_MS, update, 0);
}

void handleResize(int w, int h) {
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, (double)w / (double)h, 0.02, 100.0);
}
int main(int argc, char** argv) {
	
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(800, 600);

	glutCreateWindow("A Game: Space Invaders");
	initRendering();

	thePlayer = new Player();
	theEnemyManager = new EnemyManager();
	theEnemyManager->Initialize();

	glutDisplayFunc(drawScene);
	glutKeyboardFunc(handleKeypress);
	glutKeyboardUpFunc(handleKeyReleased);
	glutSpecialFunc(handleSpecialKeypress);
	glutSpecialUpFunc(handleSpecialKeyReleased);
	glutReshapeFunc(handleResize);
	glutTimerFunc(TIMER_MS, update, 0);

	glutMainLoop();
	return 0;
}

